HVM合约内置方法和工具方法使用手册

1. 合约内置变量

目前合约的内置变量

  • sender :合约方法调用者的地址(当且仅当存在跨合约调用时值会与orgin不同)
  • origin :合约调用者的地址
  • txHash :当前交易的哈希
  • blockTimestamp :交易所在区块的时间戳
  • blockNumber :交易所在区块
  • contractAddress :合约地址

2. 合约内置方法

合约内置方法是HVM合约实现类继承自BaseContract的方法,可在合约主类中通过this引用直接使用。

获取交易extra信息

getValueFromExtra

获得当前交易的extra字段信息,其中extra记录了链上额外的存证信息,是一个json字符串。

public String getValueFromExtra(String path) {}
方法 参数 返回值
getVal ueFromExtra path:对得 到的extra字段进行过滤的规则 String类型 ,从extra字段得到的信息

其中path是一系列由 . 分隔的key组成的字符串。

在key中可以包含一些特殊的通配符比如 *? 。如果要访问数组的指定元素,使用下标作为key。要获取数组的长度或者要基于数组添加子path,可以使用 #

你可以对一个数组使用条件查询, #[…] 将返回第一个匹配元素, #[…]# 将返回所有匹配元素,其中查询条件支持比较运算符: ==!= , <<=>>= ,还有一些简单的模式匹配: % (like)和 !% (not like)。

注意: .?* 可以通过 \ 转义成为一般字符。

实例:

{"name": {"first": "Tom", "last": "Anderson"},"age":37,"children": ["Sara","Alex","Jack"],"fav.movie": "Deer Hunter","friends": [{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Roger", "last": "Craig", "age": 68},{"first": "Jane", "last": "Murphy", "age": 47}]}
Path Result 说明
“name.last” Anderson name的属性
“age” 37 age属性
“children” [“Sara”,“Al ex”,“Jack”] children属性
“children.#” 3 children数组的长度
“children.1” “Alex” children数组的下标为1的元素
c?ildren.0” “Sara” 匹配模式 为“c?ildren”的元素的下标为0的元素
“fav.movie” “Deer Hunter” 转义.,“fav.movie”属性
“friends.#.first” [ “Dale”,“Rog er”,“Jane”] friends数组内每个元素的first属性
“friends.1.last” “Craig” friends数组的第2个元素的last属性
“friends.#[last= =”Murphy”].first” “Dale” friends数组中第一个满足last 属性等于“Murphy”的元素的first属性
“friends.#[last== ”Murphy”]#.first” [“Da le”,“Jane”] friends数组中所有满足last 属性等于“Murphy”的元素的first属性
“friends .#[age>45]#.last” [“Craig ”,“Murphy”] friends数组中所有 满足age属性大于45的元素的last属性
“friends.#[ first%”D*”].last” “Murphy” friends数组中第一个满足:first属 性符合”D*“模式,的元素的last属性
“friends.#[f irst!%”D*”].last” “Craig” f riends数组中第一个不满足:first属 性符合”D*“模式,的元素的last属性

示例:

String extra = this.getValueFromExtra("friends.1.last");

合约事件推送

hvm为合约的开发者提供了合约事件推送的功能,即可以在合约的执行过程中,将事件推送给客户端。订阅了相关事件类型的客户端可以接收到该事件消息。

合约事件的推送

合约事件推送的功能由合约基础类BaseContract实现,提供的接口如下:

public final void event(Object data, String name, String... topics);

参数:

  • data:指定的合约推送数据,最后会转换json的数据的形式推送给客户端
  • name:事件的名称,必须提供一个非空的字符串来作为标识。
  • topics:用来事件的类型。用户订阅时,可以指定自己想要接收的topic来精确订阅相应的事件。通过topics来确定要接收哪些类型的事件。

示例:

Map<String, String> map = new HashMap<String, String>();
map.put("tom","99");
map.put("bob","98");
event(map, "testEvent", "topicA");

合约事件的订阅

用户可以通过LiteSdk提供的API来订阅合约事件,合约事件必须通过MQParam来注册订阅队列。

MQParam对象的创建示例如下:

ArrayList<String> array = new ArrayList<String>();
array.add("SignedMQBlock");
array.add("MQLog");
array.add("MQException");
String qname = "node1queue";
MQParam mqParam = new MQParam.Builder().
       //队列订阅相关的参数
       msgTypes(array).     //表示要订阅的消息类型
       queueName(qname).    //表示队列名称
       // MQlog事件订阅需要的参数
       logFromBlock("1").       //表示需要推送log事件的起始区块号
       logToBlock("2").         //表示需要推送log事件的终止区块号
       logAddress("0x12...").   //表示log事件需要匹配的合约地址
       logTopics(new String[]{"topicA", "topicB"}).
       //表示log事件需要匹配的topic集合
       logTopics(new String[]{"topicC", "topicD"}).
       //可多次调用添加多个topic数组
       build();

详细的订阅规则请查看LiteSdk使用文档的 第七章. MQ相关接口(MQService) 以及Litedk的 MQ使用手册

获取内置变量

获取合约方法调用者地址

getSender

得到sender,合约方法调用者的地址(当且仅当存在跨合约调用时值会与orgin不同)

public final String getSender() {}
方法 参数 返回值
getSender sender,即合约方法调用者的地址

示例:

String sender = this.getSender();

获取合约调用者地址

getOrigin

得到origin,合约调用者的地址:

public final String getOrigin() {}
方法 参数 返回值
getOrigin origin,即合约调用者的地址

示例:

String origin = this.getOrigin();

获取交易哈希

getTxHash

得到txHash,当前交易哈希

public final String getTxHash() {}
方法 参数 返回值
getTxHash txHash,即当前交易的hash

示例:

String txHash = this.getTxHash();

获取时间戳

getBlockTimestamp

得到blockTimestamp,交易所在区块的时间戳

public final long getBlockTimestamp() {}
方法 参数 返回值
getBlockTimestamp blockTimestamp,即交易所在区块的时间戳

示例:

long blockTimestamp = this.getBlockTimestamp();

获取区块号

getBlockNumber

得到blockNumber,交易所在的区块号

public final long getBlockNumber() {}
方法 参数 返回值
getBlockNumber blockNumber,即交易所在区块号

示例:

long blockNumber = this.getBlockNumber();

获取合约地址

getContractAddress

得到contractAddress,合约地址

public String getContractAddress() {}
方法 参数 返回值
getContractAddress contractAddress,即合约地址

示例:

String contractAddress = this.getContractAddress();

合约内置方法使用demo

【源码包可参考HVM使用手册 - HVM合约Demo附件源码- hvm-manual-demo的contractMethodDemo目录】

3. HVM合约工具方法

概述

本文档介绍HVM合约编写过程可能使用到的工具方法,将分为以下8类进行说明。

  • ByteUtil
  • CryproUtil
  • HashUtil
  • ObjectsUtil
  • StringUtil
  • Logger
  • DIDUtil
  • Hyperson

ByteUtil

byte数组与Hex String转换

bytesToHex

byte数组转换为十六进制String类型

public static native String bytesToHex(byte[] input);
方法 参数 返回值
bytesToHex input:需要转换的byte数组 十六进制String

示例:

byte[] input = new byte[]{'a','b'};
String hexString = ByteUtil.bytesToHex(byte[] input);

fromHexString

十六进制String类型转换为byte数组

public static native byte[] fromHexString(String s);
方法 参数 返回值
fromHexString s:需要转换的十六进制String byte[]类型

示例:

String s1 = "0xabd43f";
ByteUtil.fromHexString(s1);

String s2 = "abd43f";
byte[] bytes = ByteUtil.fromHexString(s2);
//结果相同

byte数组和int转换

bytesToInt

byte数组转换为int类型

public static int bytesToInt(byte[] bytes) {}
方法 参数 返回值
bytesToHex input:需要转换的byte数组 十六进制String

示例:

byte[] bytes = new byte[]{'a','b','c','d'};
int result = ByteUtil.bytesToInt(bytes);

intToBytes

int类型转换为byte数组

public static byte[] intToBytes(int value) {}
方法 参数 返回值
intToBytes value:需要转换的int类型 byte[] 类型

示例:

int value = 48;
byte[] intBytes = ByteUtil.intToBytes(value);

base64编码转byte数组

fromBase64Str

将base64编码的String转换为byte数组

public native static byte[] fromBase64Str(String string);
方法 参数 返回值
fromBase64Str string:需要转换的base64编码的String类型 byte[] 类型

示例:

byte[] bytes = ByteUtil.fromBase64Str(string);

CryptoUtil

ECDSA账户验签

verifySignature

EC账户验签,通过公钥、原文和签名内容来验证签名内容。

public static boolean verifySignature(byte[] addr, byte[] origin, byte[] signature) {}
方法 参数 返回值
fromBase64Str string:需要转换的base64编码的String类型 byte[] 类型

示例:

//以下代码使用了一些litesdk的工具类
ECKey ecKey = new ECKey(new SecureRandom());
byte[] address = ecKey.getAddress();
byte[] origin = "hyperchain".getBytes();
byte[] hash = HashUtil.sha3(origin);
byte[] signature = ecKey.sign(hash).toByteArray();
CryptoUtil.verifySignature(address, hash, signature);

SM2国密账户验签

verifySM2Signature

SM账户验签,通过公钥、原文和签名内容来验证签名内容是否正确。

public static boolean verifySM2Signature(byte[] pubKey, byte[] origin, byte[] signature) {}
方法 参数 返回值
verifySM2 Signature pubKey:byte[] 类型的公钥origin:byte[]类 型的原文signature:byte[]类型的签名。 布尔值,签名正 确则返回true, 否则返回false。

示例:

//以下代码使用类litesdk的工具类
AsymmetricCipherKeyPair keyPair = SM2Util.generateKeyPair();
ECPublicKeyParameters ecPub;
ecpub = (ECPublicKeyParameters) keyPair.getPublic();
byte[] origin = "hyperchain".getBytes();
byte[] publicKey = ecPub.getQ().getEncoded(false);
byte[] signature = SM2Util.sign(keyPair, origin);
CryptoUtil.verifySM2Signature(publicKey,origin,signature);

SM4加解密

sm4Encrypt

sm4加密:

public static byte[] sm4Encrypt(byte[] key, byte[] src) {}
方法 参数 返回值
sm4Encrypt key:16字节的 对称加密密钥src:需要被加密的内容 byte[] 类型被加密的密文

sm4Decrypt

sm4解密:

public static byte[] sm4Decrypt(byte[] key, byte[] src) {}
方法 参数 返回值
sm4Decrypt key:16字节的 对称加密密钥src:需要被解密的内容 byte[] 类型被解密的原文

示例:

byte[] key = new byte[]{'e','h','r','s','e','h','r','s','e','h','r','s','e','h','r','s'};
byte[] src = "hyperchain".getBytes();
byte[] encryptBytes = CryptoUtil.sm4Encrypt(key, src);
byte[] decryptBytes = CryptoUtil.sm4Decrypt(key, encryptBytes);
ByteUtil.bytesToHex(src).equals(ByteUtil.bytesToHex(decryptBytes));

AES加解密

aesEncrypt

AES加密:

public static byte[] aesEncrypt(byte[] key, byte[] src) {}
方法 参数 返回值
aesEncrypt key:32字节的 对称加密密钥src:需要被加密的内容 byte[] 类型被加密的密文

aesDecrypt

AES解密:

public static byte[] aesDecrypt(byte[] key, byte[] src) {}
方法 参数 返回值
aesDecrypt key:32字节的 对称加密密钥src:需要被解密的内容 byte[] 类型被解密的原文

示例:

keyStr = keyStr.substring(0, 32);
byte[] key = keyStr.getBytes();
byte[] src = "hyperchain".getBytes();
byte[] encryptBytes = CryptoUtil.aesEncrypt(key, src);
byte[] decryptBytes = CryptoUtil.aesDecrypt(key, encryptBytes);
ByteUtil.bytesToHex(src).equals(ByteUtil.bytesToHex(decryptBytes));

tripleDES加解密

tripleDESEncrypt

tripleDES 加密:

public static byte[] tripleDESEncrypt(byte[] key, byte[] src) {}
方法 参数 返回值
tripleDESEncrypt key:24字节的对称 加密密钥src:需要被加密的内容 byte[] 类 型被加密的密文

tripleDESDecrypt

tripleDES 解密:

public static byte[] tripleDESDecrypt(byte[] key, byte[] src) {}
方法 参数 返回值
tripleDESDecrypt key:24字节的对称 加密密钥src:需要被解密的内容 byte[] 类 型被解密的原文

示例:

keyStr = keyStr.substring(0, 24);
byte[] key = keyStr.getBytes();
byte[] src = "hyperchain".getBytes();
byte[] encryptBytes = CryptoUtil.tripleDESEncrypt(key, src);
byte[] decryptBytes = CryptoUtil.tripleDESDecrypt(key, encryptBytes);
ByteUtil.bytesToHex(src).equals(ByteUtil.bytesToHex(decryptBytes));

ecc加密

eccEncrypt:

public static byte[] eccEncrypt(byte[] publicKey, byte[] src) {
方法 参数 返回值
eccEncrypt key:加密密钥src:需要被加密的内容 byte[] 类型被加密的原文

CryptoUtil使用demo

【源码包可参考HVM使用手册 - HVM合约Demo附件源码 -** hvm-manual-demo的cryptoCallDemo目录**】

HashUtil

sha3-256哈希计算

sha3

对输入的byte数组进行sha3-256哈希计算,返回散列值:

public static byte[] sha3(byte[] input) {}
方法 参数 返回值
sha3 input:需要哈希的byte数组 byte[] 类型散列值

示例:

byte[] input = new byte[]{'a','b','c','d'};
byte[] hashResult = HashUtil.sha3(input);

ObjectsUtil

判断对象是否相等

equals

判断Object是否相等,返回布尔值:

public static boolean equals(final Object x, final Object y) {
方法 参数 返回值
equals x:Java Object。y:Java Object 布尔值,x和y相等, 返回true,否则返回false。

示例:

String x = new String("test");
Integer y = new Integet(0);
boolean isEqual = ObjectsUtil.equals(x, y);

哈希计算

hash

计算多个Object的哈希值,返回多个Object的哈希值:

public static int hash(final Object... values) {}
方法 参数 返回值
hash values:0个或多个Java Object。 多个Object的哈希值

示例:

String object1 = new String("test");
Integer object2 = new Integet(0);
int hashResult0 = ObjectsUtil.hash();
int hashResult1 = ObjectsUtil.hash(object1);
int hashResult2 = ObjectsUtil.hash(object1, object2);

StringUtil

检查String对象是否为空

checkEmpty

public static boolean checkEmpty(String string) {}
方法 参数 返回值
checkEmpty string: 需检查的String对象 布尔值,String对象 为空则返回true,否则返回false。

示例:

String string = new String();
boolean isEmpty = StringUtil.checkEmpty(string);

Logger

Logger类提供了打印对应classlog信息的功能。

获取logger

getLogger

public static Logger getLogger(Class clazz) {}
方法 参数 返回值
getLogger clazz: 对应的class对象 Logger对象

示例:

public class InvokeTripleDES implements BaseInvoke<Boolean, ICrypto> {
   private Logger logger = Logger.getLogger(InvokeTripleDES.class);
}

日志级别

critical级别的日志:

public void critical(Object message) {}
方法 参数 返回值
critical message: Object对象,需打印的日志信息

示例:

logger.critical("logger critical message");

err级别的日志:

public void err(Object message) {}
方法 参数 返回值
err message: Object对象,需打印的日志信息

示例:

logger.err("logger err message");

warning级别的日志:

public void warning(Object message) {}
方法 参数 返回值
warning message: Object对象,需打印的日志信息

示例:

logger.warning("logger warning message");

notice级别的日志:

public void notice(Object message) {}
方法 参数 返回值
notice message: Object对象,需打印的日志信息

示例:

logger.notice("logger notice message");

info级别的日志:

public void info(Object message) {}
方法 参数 返回值
info message: Object对象,需打印的日志信息

示例:

logger.info("logger info message");

debug级别的日志:

public void debug(Object message) {}
方法 参数 返回值
debug message: Object对象,需打印的日志信息

示例:

logger.debug("logger debug message");

DIDUtil

检查凭证是否有效

credentialIsValid:

public static native boolean credentialIsValid(String creID);
方法 参数 返回值
credent ialIsValid string: 需检查的凭证ID 布尔值,凭证被 吊销或过期则返回false,否则返回true。

示例:

boolean isValid = DIDUtil.credentialIsValid(creID)

检查凭证是否吊销

credentialIsAbondoned:

public static native boolean credentialIsAbondoned(String creID);
方法 参数 返回值
credent ialIsAbondoned string: 需检查的凭证ID 布尔值,凭证 被吊销则返回true,否则返回false。

示例:

boolean isValid = DIDUtil.credentialIsAbondoned(creID)

Hyperson

Hyperson提供了 toHyperson 方法将Java对象序列化成json字符串,以及 fromHyperson 方法将json字符串反序列化成Java对象。

在使用Hyperson提供的序列化和反序列化方法之前,首先要构造一个Hyperson对象,可通过构造方法以及HypersonBuilder来构建,示例如下:

// 无参构造函数, 配置均按照默认值
Hyperson hyperson = new Hyperson();

// 有参构造函数,可传入相关配置
Hyperson hyperson = new Hyperson(escapeHtml, serialzeNull, excludeAnnotationField, excludeClassField);

// HypersonBuilder构建
Hyperson hyperson = new Hyperson.HypersonBuilder()
       .disableEscapeHtml()
       .enableSerializeNull()
       .addExcludeAnnotationField(StoreField.class)
       .addExcludeClassField(PersonName.class)
       .create();

配置项说明如下:

  • serializeNull 控制是否序列化对象中值为null的字段,设置为true,则序列化值为null的字段;设置为false,则忽略值为null的字段,默认为false。
  • escapeHtml 控制是否转义html字符,如 < ,设置为true,则对html字符进行转义;设置为false,则不对html字符转义,默认为true。
  • excludeAnnotationField 记录了注解类型,序列化和反序列化的过程中,将会忽略添加了这些注解的字段。即如果不想序列化对象中的某些字段,可以给相应字段添加注解,并将该注解类型设置到excludeAnnotationField中,那么添加了该注解的对象字段将不会被序列化。
  • excludeClassField 记录了不被序列化及反序列化的字段类型。即如果不想序列化某一类型的字段,则可以将该类型设置到excludeClassField中,那么该类型的字段将不会被序列化。

序列化

toHyperson:

public String toHyperson(Object object);
方法 参数 返回值
toHyperson object: 需序列化的对象 字符串,对象序列化后的json字符串

示例:

Hyperson hyperson = new Hyperson();
Student student = new Student();
String json = hyperson.toHyperson(student);

反序列化

fromHyperson:

public <T> T fromHyperson(String hyperson, Type type);
方法 参数 返回值
fromHyperson hyperson: 对象的json字符串type:对象的类型 反 序列化生成 的对象实例

示例:

Hyperson hyperson = new Hyperson();

String json = "test";
Type type = String.class;
String res = hyperson.fromHyperson(json, type);

String mapJson = "{\"key\":\"value\"}";
Type mapType = new ParameterizedTypeImpl(HashMap.class, new Type[]{String.class, String.class}, null);
Map mapRes = hyperson.fromHyperson(mapJson, mapType);

注意事项

  • 对象中不允许存在同名字段,即父类和子类不能存在同名字段
  • 序列化Map类型的对象时,其key不能为复杂类型,可为八大基本类型或String类型
  • 反序列化时,传入的类型参数必须是class类型对象或者实现了ParameterizedType接口的对象
  • 反序列化时,如果没有传入相应的类型参数,则数组、集合对象默认反序列化为ArrayList;byte、short、int、long、float、double默认为Double;char、String默认为String;boolean默认为Boolean;其他对象反序列化为LinkedHashMap对象
  • 反序列化时,当传入的类型参数是抽象类或接口时,如果其为继承或实现Collection的接口或抽象类类型,则SortedSet接口类型对应TreeSet类型,EnumSet类型对应EnumSet的子类,Set接口类型对应LinkedHashSet类型,Queue接口类型对应ArrayDeque类型,其他为ArrayList类型;如果其为继承或实现Map的接口或抽象类类型,则ConcurrentNavigableMap接口类型对应ConcurrentSkipListMap类型,ConcurrentMap接口类型对应ConcurrentHashMap类型,SortedMap接口类型对应TreeMap类型,其他为LinkedHashMap类型。如果不为Map、Collection以及Number,其他抽象类或接口会反序列化失败。
  • 浮点数不允许为NaN或者inf(无穷值)
  • Hyperson不支持对增加了注释的json字符串进行反序列化